<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Exception\Handler;

use App\Exception\BusinessException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Throwable;
use Xin\Hint\Facades\Hint;
use Xin\Logger\Logger;
use function Hyperf\Config\config;

class AppExceptionHandler extends ExceptionHandler
{
    /**
     * @var LoggerInterface
     */
    protected LoggerInterface $logger;

    /**
     * AppExceptionHandler constructor.
     */
    public function __construct()
    {
        $this->logger = Logger::logger()->enableStdout();
    }

    /**
     * @param Throwable $throwable
     * @param ResponseInterface $response
     * @return MessageInterface|ResponseInterface
     */
    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
        $this->logger->error($throwable->getTraceAsString());

        $response = $response->withHeader('content-type', 'application/json; charset=utf-8');

        if ($throwable instanceof BusinessException) {
            return $response->withStatus(400)->withBody(new SwooleStream(
                Hint::error($throwable->getMessage(), $throwable->getCode())
            ));
        } elseif ($throwable instanceof HttpException) {
            if ($throwable->getStatusCode() == 401) {
                return $response->withStatus(401)->withBody(new SwooleStream(
                    Hint::error('Unauthorized.', -1)
                ));
            } elseif ($throwable->getStatusCode() == 403) {
                return $response->withStatus(403)->withBody(new SwooleStream(
                    Hint::error('Forbidden.', $throwable->getStatusCode())
                ));
            } elseif ($throwable->getStatusCode() == 404) {
                return $response->withStatus(404)->withBody(new SwooleStream(
                    Hint::error('Not Found.', $throwable->getStatusCode())
                ));
            } elseif ($throwable->getStatusCode() == 405) {
                return $response->withStatus(405)->withBody(new SwooleStream(
                    Hint::error('Method Not Allowed.', $throwable->getStatusCode())
                ));
            }
        }

        if (config('app_env') == 'dev') {
            return $response->withStatus(500)->withBody(new SwooleStream(
                Hint::error($throwable->getMessage(), 500, null, [
                    'exception' => get_class($throwable),
                    'traces' => $throwable->getTrace(),
                ])
            ));
        }

        return $response->withStatus(500)->withBody(new SwooleStream(
            Hint::error('Internal Server Error.', 500)
        ));
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}
